﻿// -----------------------------------------------------------------------
// <copyright file="MyClass.cs" company="none">
// Copyright (c) 2012 M. Alberti, xanatos(at)live.it
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// </copyright>
// -----------------------------------------------------------------------

namespace FluentSerializer.Sample
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using FluentSerializer;
    using FluentSerializer.CollectionsHelpers;
    using FluentStatement;

    /// <summary>
    /// 
    /// </summary>
    [FluentSerializable(typeof(MyClass.Serializer))]
    public class MyClass : MySimpleClass
    {
        /// <summary>
        /// 
        /// </summary>
        public static readonly int[] StaticReadonlyInts1 = new int[] { 0, 1, 2, 3, 4, 5 };

        /// <summary>
        /// 
        /// </summary>
        public readonly int[] ReadonlyInts1 = new int[] { 0, 1, 2, 3, 4, 5 };

        /// <summary>
        /// 
        /// </summary>
        public readonly List<int> ReadonlyList1 = new List<int> { 0, 1, 2, 3, 4, 5 };

        /// <summary>
        /// 
        /// </summary>
        public readonly int ReadonlyInt1;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="readonlyInt1"></param>
        public MyClass(int readonlyInt1)
        {
            this.ReadonlyInt1 = readonlyInt1;
        }

        /// <summary>
        /// 
        /// </summary>
        public int Version { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public bool Bool1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int Int2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int Int3 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public short Short1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public Tuple<int, Tuple<short, bool>> Tuple1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int[] Ints1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int[] Ints2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int[] Ints3 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int? NullableInt1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int? NullableInt2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int[] OptionalInts1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int[] OptionalInts2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public List<int> OptionalList1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public List<int> OptionalList2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public DateTime DateTime1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public DateTime DateTime2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public float Float1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public double Double1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public decimal Decimal1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public TimeSpan TimeSpan1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public string String1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public string String2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public string String3 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public new class Serializer : ISerializer<MyClass, EmptyParameters>
        {
            #region ISerializer<MyClass,EmptyParameters> Members

            /// <summary>
            /// 
            /// </summary>
            /// <returns></returns>
            public Expression<Serializer<MyClass, EmptyParameters>> GetSerializer()
            {
                return (op, obj, par) => Statement.Start(fl => fl
                    .Serialize(obj.Version)

                    // Static objects can be serialized/deserialized.
                    .Serialize(MyClass.StaticReadonlyInts1, typeof(FixedLength<>))

                    // So can readonly collections.
                    .Serialize(obj.ReadonlyInts1, typeof(FixedLength<>))

                    // Both array and List<> (and Dictionary<,>, and SortedDictionary<,>, and
                    // many other types of collections)
                    ////.Serialize(obj.ReadonlyList1)
                    .Serialize(obj.ReadonlyList1, typeof(VariableLengthByte<>))

                    ////// Readonly fields can be serialized/deserialized.
                    ////// Sadly you can't Dump() serializers that replace read only fields
                    ////// (replace is the keyword here, readonly int X is a no-no, 
                    ////// readonly List<int> X is a yes, readonly int[] X is a yes if it's 
                    ////// FixedLength<>.
                    ////.Serialize(obj.ReadonlyInt1)

                    .Serialize(obj.Bool1)
                    .Serialize(obj.Int2)

                    // This will be serialized/deserialized only if obj.Version != 0
                    // It's only an example of what you can do. You can use the full power of
                    // FluentStatement, and remember that if instead of EmptyParameters you
                    // had used another class as the parameters, you could have manipulated it
                    // through the par object, so par.Version for example.
                    .If(obj.Version != 0, fl.Serialize(obj.Int3))

                    // This if(s) depend on the operation that is being done
                    // (serialization/deserialization/size)
                    .If(op == Operation.Serialize, fl.Serialize(obj.Int2))
                    .If(op == Operation.Deserialize, fl.Serialize(obj.Int3))

                    .Serialize(obj.Short1)

                    // Tuples are supported.
                    .Serialize(obj.Tuple1)

                    // Arrays need to have the length prepended. There are helpers for this.
                    // The default helper can be specified in the Serializer<T> constructor and
                    // will be used if the field serializer isn't specified.
                    ////.Serialize(obj.Ints1)

                    // Or you can specify it:
                    .Serialize(obj.Ints2, typeof(VariableLengthByte<>))
                    .Serialize(obj.Ints3, typeof(VariableLengthByte<int[]>))

                    // Nullable types are supported
                    .Serialize(obj.NullableInt1, typeof(Nullable<int>))
                    ////.Serialize(obj.NullableInt2)

                    // But note that you could even use the Optional<> with value types,
                    // usefull for example if you have to use a modifier that is a class
                    // (like VariableLengthInt32 for example)
                    .Serialize(obj.NullableInt1, typeof(Optional<int>))
                    .Serialize(obj.NullableInt2, typeof(Optional<>))

                    // So are "optional" objects (fields that can be null)
                    // (Note that here if we wanted to specify the helper, we would have
                    // to use typeof(Optional<VariableLengthByte<int>>)
                    .Serialize(obj.OptionalInts1, typeof(Optional<VariableLengthInt32<int[]>>))
                    .Serialize(obj.OptionalInts2, typeof(Optional<>))
                    .Serialize(obj.OptionalList1, typeof(Optional<VariableLengthInt32<List<int>>>))
                    .Serialize(obj.OptionalList2, typeof(Optional<>))

                    // You can serialize a DateTime as the full .NET value
                    .Serialize(obj.DateTime1)

                    // Or, for example, as an Unix datetime (32 or 64 bits)
                    .Serialize(obj.DateTime2, typeof(UnixDateTime<int>))

                    .Serialize(obj.Float1)
                    .Serialize(obj.Double1)
                    .Serialize(obj.Decimal1)
                    .Serialize(obj.TimeSpan1)

                    // For strings it's a little more complex. There are too many combinations 
                    // of possible formats (encoding x string length * (use char or byte length))
                    // At this time there isn't any helper for C strings (null terminated strings).
                    // You have to "manually" register you string formats.
                    .Serialize(obj.String1, typeof(Program.MyUtf8VariableLengthInt32String))
                    .Serialize(obj.String2, typeof(Program.MyAsciiVariableLengthInt32String))
                    .Serialize(obj.String3, typeof(Program.MyUnicodeVariableLengthInt32String))

                    // Chain serializing the base class can be done in this way
                    .Serialize(obj, typeof(MySimpleClass))

                    // This is only to make it easy to add new serialization fields. The last ) is
                    // "attached" to the .Empty and doesn't need to be moved.
                    .Empty());
            }

            #endregion
        }
    }
}
